package slap.template;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import jodd.io.StreamUtil;
import jodd.io.findfile.FilepathScanner;

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import slap.Resources;
import slap.Slap;
import slap.annotation.AfterFrameworkInit;
import slap.annotation.Scan;
import slap.exception.NotFoundException;
import slap.http.Request;

@Scan
public class Template {
	protected static final Logger logger = LoggerFactory.getLogger(Template.class);
	protected static VelocityEngine ve;
	
	@AfterFrameworkInit
	public static void init() {
		ve = new VelocityEngine();
	    ve.setProperty(
	    		RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
	      		"org.apache.velocity.runtime.log.Log4JLogChute" 
	    );
	    ve.setProperty("runtime.log.logsystem.log4j.logger",
	    		getLoggerName() );
		ve.setProperty(RuntimeConstants.INPUT_ENCODING, Slap.ENCODING);
		ve.setProperty(RuntimeConstants.OUTPUT_ENCODING, Slap.ENCODING);
		ve.setProperty(RuntimeConstants.DEFAULT_RUNTIME_DIRECTIVES, Slap.ENCODING);
		ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");
		ve.setProperty(
				"file.resource.loader.description",
				"Generic Velocity Template Loader"
		);
		ve.setProperty(
				"file.resource.loader.class",
				"org.apache.velocity.runtime.resource.loader.FileResourceLoader"
		);
		ve.setProperty(
				"file.resource.loader.path", 
				getLoaderPath()
		);
		ve.setProperty("file.resource.loader.isCachingOn", true);
		ve.setProperty("file.resource.loader.cache", false);
		ve.setProperty(
				"eventhandler.include.class", 
				"org.apache.velocity.app.event.implement.IncludeRelativePath"
		);
		ve.setProperty(
				RuntimeConstants.VM_LIBRARY, 
				getVMLibrary() 
		);
		
		ve.init();
		
	}
	
	protected void precompile(){
		final int PL = getLoaderPath().length()-1;
		final StringWriter sw = new StringWriter();
		final VelocityContext nullContext = new VelocityContext();
		FilepathScanner fs = new FilepathScanner(){
			@Override
			protected void onFile(File file) {
				if(!file.getName().endsWith(".vm")){
					return;
				}
				String vm = file.getAbsolutePath().substring(PL);
				ve.getTemplate(vm).merge(nullContext, sw);
			}
		};
		fs.recursive(true).includeDirs(false).includeFiles(true);
		try {
			fs.scan(getLoaderPath());
		} finally{
			StreamUtil.close(sw);
		}
	}
	
	protected static String getLoaderPath(){ 
		return Resources.Presets.DIR_STATIC;
	}
	
	protected static String getLoggerName(){ 
		return "VelocityRender";
	}
	
	protected static String getVMLibrary(){ 
		return "";
	}
	
	public static org.apache.velocity.Template _load(String templateName){
		try{
			return ve.getTemplate(templateName);
		}catch(Exception ex){
			return null;
		}
	}
	
	public static void render(String templateName, Map<String, Object> env, Writer writer) {
		try{
			_load(templateName).merge(
					new VelocityContext(env), 
					writer
			);
			writer.flush();//Important !!
		}catch(Exception ex){
			ex.printStackTrace();
		}
	}
	
	public static void render(String templateName, Map<String, Object> env, OutputStream out) {
		render(templateName, env, new OutputStreamWriter(out));
	}
	
	public static StringWriter render(String templateName, Map<String, Object> env) {
		StringWriter writer = new StringWriter();
		render(templateName, env, writer);
		return writer;
	}

}